package com.lincoln.framework;

import com.lincoln.framework.bean.ProtocolContext;
import com.lincoln.framework.exception.ErrorFormatException;
import com.lincoln.framework.exception.ExecuteException;
import com.lincoln.framework.exception.ExecuteParamWrongException;
import com.lincoln.framework.exception.NettyException;
import com.lincoln.framework.factories.ProtocolFactory;
import com.lincoln.framework.factories.ProtocolFactoryBean;
import com.lincoln.framework.interfaces.BaseDeviceInfo;
import com.lincoln.framework.interfaces.BaseExecute;
import com.lincoln.framework.interfaces.BaseProtocol;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.UUID;

/**
 * 事件响应
 *
 * @author lincoln
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    private static Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);

    /**
     * 内部为了找到客户端使用的客户端id
     */
    private String clientId = null;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            BaseProtocol protocol = (BaseProtocol) msg;
            if (!protocol.verify()) {
                throw new ErrorFormatException("收到的消息格式错误!");
            }
            BaseDeviceInfo deviceInfo = ProtocolContext.getDevice();
            deviceInfo.setMsgSender(ctx);
            //时时更新ip与端口号
            deviceInfo.setIp(((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress());
            deviceInfo.setPort(((InetSocketAddress) ctx.channel().remoteAddress()).getPort());
            //请求次数+1
            deviceInfo.getAttributes().put(BaseDeviceInfo.REQUEST_TIME, (int) (deviceInfo.getAttributes().get(BaseDeviceInfo.NO_LOGIN_REQUEST_TIME) == null ? 0 : deviceInfo.getAttributes().get(BaseDeviceInfo.REQUEST_TIME)) + 1);
            //获取对应协议
            ProtocolFactoryBean protocolFactory = ProtocolFactory.getProtocol(deviceInfo.getDeviceType(), deviceInfo.getProtocolType());

            if (!deviceInfo.isLogin()) {
                //未登录请求次数+1
                deviceInfo.getAttributes().put(BaseDeviceInfo.NO_LOGIN_REQUEST_TIME, (int) (deviceInfo.getAttributes().get(BaseDeviceInfo.NO_LOGIN_REQUEST_TIME) == null ? 0 : deviceInfo.getAttributes().get(BaseDeviceInfo.NO_LOGIN_REQUEST_TIME)) + 1);
                BaseExecute execute = ProtocolFactory.defaultExecute;
                if (execute != null) {
                    if (execute.execute(protocol)) {
                        ProtocolContext.removeNoLogin(clientId);
                        ProtocolContext.addClient(deviceInfo);
                        deviceInfo.login();
                    }
                } else {
                    throw new NettyException("未找到对应的执行器");
                }
            } else {
                // TODO: 2018/11/30 此节点要增加灵活的执行器,决定是否要继续向下执行

                BaseExecute execute = null;
                if (protocolFactory != null) {
                    execute = protocolFactory.getExecute(protocol.getCmdText());
                }
                if (execute == null) {
                    // TODO: 2018/11/30 此节点要增加灵活的执行器,加入一些默认的执行器
                    throw new NettyException("未找到对应的执行器");
                } else {
                    execute.execute(protocol);
                }
            }
        } catch (ExecuteParamWrongException e) {
            logger.error("执行器检测到参数错误", e);
        } catch (ExecuteException e) {
            logger.error("执行出现异常:", e);
        } catch (Exception e) {
            logger.error("异常:", e);
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    /**
     * 心跳机制
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            //超时读取
            if (event.state() == IdleState.READER_IDLE) {
                logger.info("心跳超时,服务器主动关闭");
                ctx.channel().close();
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    /**
     * 异常处理
     *
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        if (cause instanceof IOException) {
            //do nothing
        } else {
            logger.error("", cause);
        }
        ctx.close();
    }


    /**
     * 新客户端连接后触发
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws IllegalAccessException, InstantiationException {
        if (clientId == null) {
            clientId = UUID.randomUUID().toString();
            BaseDeviceInfo deviceInfo = NettyServer.getNettyServer().getDeviceInfo().newInstance();
            deviceInfo.setClientId(clientId);
            deviceInfo.setIp(((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress());
            deviceInfo.setPort(((InetSocketAddress) ctx.channel().remoteAddress()).getPort());
            ProtocolContext.addNoLogin(deviceInfo);
            ProtocolContext.setDevice(deviceInfo);
            logger.info("未认证的新客户端进入!clientId=[" + clientId + "]");
        }
        // TODO: 2020/12/25 此处加入自定义执行语句

        ctx.fireChannelActive();
    }

    /**
     * 连接关闭后触发
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        if (null != clientId && !"".equals(clientId) && null != ProtocolContext.getDevice()) {
            BaseDeviceInfo deviceInfo = ProtocolContext.getDevice();
            if (deviceInfo.isLogin()) {
                logger.info("有认证的客户端退出!clientId=[" + clientId + "],primaryKey=[" + deviceInfo.getPrimaryKey() + "]");
                deviceInfo.logout();
                ProtocolContext.removeClient(deviceInfo.getDeviceType(), clientId);
            } else {
                logger.info("有未认证的客户端退出!clientId=[" + clientId + "]");
                deviceInfo.logout();
                ProtocolContext.removeNoLogin(clientId);
            }
        }
        // TODO: 2020/12/25 此处加入自定义执行语句
    }
}
